
#define ProcessListsIndex 1
#define ActiveProcessIndex 2
#define FirstLinkIndex 1
#define LastLinkIndex 2
#define ExcessSignalsIndex 3
#define NextLinkIndex 1
#define SuspendedContextIndex 2
#define PriorityIndex 3
#define MyListIndex 4
#define ValueIndex 2

#define TheTimerSemaphore 30
#define MillisecondClockMask 0x1FFFFFFF

void addLastLinkToList(int proc, int aList)
{
	//Add the given process to the given linked list and set the 
	//backpointer of process to its new list.
	int lastLink;
	
	if( isEmptyList( aList ) )
	{
		setSlotOf( aList, FirstLinkIndex, proc );
	}
	else
	{
		lastLink= slotOf(aList, LastLinkIndex);
		setSlotOf( lastLink, NextLinkIndex, proc );
	}
	
	setSlotOf( aList, LastLinkIndex, proc );
	setSlotOf( proc, MyListIndex, aList );
}

int isEmptyList(int aLinkedList)
{
	return slotOf(aLinkedList, FirstLinkIndex) == nil;
}

void putToSleep(int aProcess)
{
	//"Save the given process on the scheduler process list for its priority."
	int priority, processLists, processList;
	
	priority = unpackSmInt(slotOf(aProcess, PriorityIndex));
	processLists = slotOf(schedulerPointer(), ProcessListsIndex);
	processList = slotOf(processLists, priority);
	addLastLinkToList(aProcess, processList);
}

int removeFirstLinkOfList(int aList)
{
	//"Remove the first process from the given linked list."
	int first, last, next;
	
	first = slotOf(aList, FirstLinkIndex);
	last = slotOf(aList, LastLinkIndex);
	
	if( first == last )
	{
		setSlotOf( aList, FirstLinkIndex, nil );
		setSlotOf( aList, LastLinkIndex, nil );

	}else{ 
		next = slotOf(first, NextLinkIndex);
		setSlotOf( aList, FirstLinkIndex, next );
	}
	
	setSlotOf( first, NextLinkIndex, nil );
	return first;
}

void resume(int aProcess)
{
	int activeProc, activePriority, newPriority;
	
	activeProc = slotOf( schedulerPointer() , ActiveProcessIndex);
	activePriority = unpackSmInt(slotOf(activeProc, PriorityIndex));
	newPriority = unpackSmInt(slotOf(aProcess, PriorityIndex));
	
	//printf("active %d candidate %d\n", activePriority, newPriority);
	
	if(newPriority > activePriority)
	{
		putToSleep(activeProc);
		transferTo(aProcess);
		
	}else{
		putToSleep(aProcess);
	}
}

int schedulerPointer()
{
	return slotOf(SchedulerAssociation, ValueIndex);
}

int externalSignalBuffer[100];
int externalSignalCount=0;

void externalSignal(int n)
{
	externalSignalBuffer[externalSignalCount] = n;
	externalSignalCount++;
}

void signalExternalSemaphores()
{
	__asm__("cli");
	
	while(externalSignalCount > 0)
	{
		externalSignalCount--;
		int num = externalSignalBuffer[externalSignalCount];
		int sema = slotOf( slotOf(specialObjectsArray, 39) , num ); 
		synchronousSignal(sema);
	}
	
	__asm__("sti");
}

void synchronousSignal(int aSemaphore)
{
	//"Signal the given semaphore from within the interpreter."
	int excessSignals;

	if(isEmptyList(aSemaphore))
	{
		//"no process is waiting on this semaphore"
		excessSignals= unpackSmInt(slotOf(aSemaphore, ExcessSignalsIndex));
		setSlotOf( aSemaphore, ExcessSignalsIndex, packSmInt(excessSignals+1) );
	}
	else
	{
		resume( removeFirstLinkOfList(aSemaphore) );
	}
}

void transferTo(int aProc )
{
	//"Record a process to be awoken on the next interpreter cycle. 
	int sched, oldProc, newProc;

#ifdef PROGRAM	
	int newPriority = unpackSmInt(slotOf(aProc, PriorityIndex));
	printf("PROCESS SWITCH to ");
	printShortObject( slotOf(aProc, 6) );
	printf(" with priority %d @ %p\n", newPriority, aProc);
#endif

	newProc= aProc;
	sched= schedulerPointer();
	oldProc= slotOf(sched, ActiveProcessIndex);
	setSlotOf(sched, ActiveProcessIndex, newProc);
	
	setSlotOf( oldProc, SuspendedContextIndex, activeContext);
	newActiveContext( slotOf(newProc, SuspendedContextIndex) );
	setSlotOf(newProc, SuspendedContextIndex, nil);
			
	recyclableDepth=0;
}

void newActiveContext(int aContext)
{
	saveContextRegisters();
	loadContextRegistersFrom(aContext);
}
	
int wakeHighestPriority()
{
	//"Return the highest priority process that is ready to run."
	//"Note: It is a fatal VM error if there is no runnable process."
	int schedLists, p, processList;
	schedLists = slotOf( schedulerPointer(), ProcessListsIndex );
	p = sizeOf(schedLists); //"index of last indexable field"
	processList= slotOf(schedLists, p);
	
	while( isEmptyList(processList) )
	{
		p--;
		if(p <= 0) PANIC("Scheduler could not find a runnable process");
		processList= slotOf(schedLists, p);
	}
	return removeFirstLinkOfList( processList );
}

//WARNING WARNING WARNING WARNING WARNING
//process primitives must adjust stack before calling helper functions
//since the active process may change and the stack pointer will be for
//the resumed context rather than the context that invoked the primitve

void primitiveResume()
{
	//"put this process on the scheduler's lists thus allowing it to proceed next time
	//there is a chance for processes of it's priority level"
	
	int proc = top();
	
	resume( proc );
}	
	
void primitiveSignal()
{
	//"synchromously signal the semaphore. This may change the active process as a result"

	int sema= top();
	
	if( classOf(sema) != Semaphore ) PANIC("Should be Semaphore");
	
	synchronousSignal( sema );
}

void primitiveSuspend()
{
	int activeProc = slotOf( schedulerPointer(), ActiveProcessIndex );
	
	if( top() != activeProc) success=0;
	
	if( success )
	{
		pop();
		push(nil); //this seems weird, the pattern of every other primitive is the return the reciever
		
		transferTo(wakeHighestPriority());
	}
}
	
void primitiveWait()
{
	int sema, excessSignals, activeProc;
	
	sema= top();
	
	if( classOf(sema) != Semaphore ) PANIC("Should be Semaphore");
	
	if(success)
	{
		excessSignals = unpackSmInt(slotOf(sema, ExcessSignalsIndex));
			
		if( excessSignals > 0 )
		{
			setSlotOf( sema, ExcessSignalsIndex, packSmInt(excessSignals - 1) );
		}
		else
		{
			activeProc = slotOf( schedulerPointer(), ActiveProcessIndex );
			
			addLastLinkToList( activeProc , sema );
			transferTo(  wakeHighestPriority() );
		}
	}
	else PANIC("!GRGA");
}



/////////NSVM

//Delay class primSignal: aSemaphore atMilliseconds: aSmallInteger
//<primitive: 136>

void primitiveSignalAtMilliseconds()
{
	//Cause the time semaphore, if one has been registered, to
	//be signalled when the millisecond clock is greater than or
	//equal to the given tick value. A tick value of zero turns off
	//timer interrupts.
	
	int sema = peek(1);
	int tick = unpackSmInt(peek(0));
	
	if(success){

		if( classOf(sema)==Semaphore ){
			setSlotOf( specialObjectsArray, TheTimerSemaphore, sema);
			nextWakeupTick= tick;
		}else{
			setSlotOf( specialObjectsArray, TheTimerSemaphore, nil);
			nextWakeupTick= 0;
		}
		
		popNandPush( argumentCount+1, rcvr );
	}
}

/////////////

void checkForInterrupts()
{
	int now= millisecond_clock & MillisecondClockMask;

	if( nextWakeupTick != 0 )
	{
		if( now < lastTick )
		{
			//"the clock has wrapped. Subtract the wrap  interval from nextWakeupTick - this might just 
			//possibly result in 0. Since this is used as a flag  
			//value for 'no timer' we do the 0 check above"
			nextWakeupTick= nextWakeupTick - MillisecondClockMask - 1;
		}
		if( now >= nextWakeupTick )
		{
			nextWakeupTick= 0;
			//"set timer interrupt to 0 for 'no timer'"
			int sema= slotOf(specialObjectsArray, TheTimerSemaphore);
			if(sema != nil) synchronousSignal( sema );
		}
	}

	if(externalSignalCount > 0) signalExternalSemaphores();
	
	lastTick= now;
}

//relinquishProcessorForMicroseconds: anInteger
void primRelinquishProcessor()
{
#ifdef PROGRAM
	usleep(50*1000); // 50 milliseconds
#endif
#ifdef KERNEL
	__asm__("hlt"); //until next interrupt
#endif
	
	popNandPush(argumentCount + 1, rcvr);
}
